package com.example.threadmodel.model;

import cn.hutool.extra.spring.SpringUtil;
import com.example.threadmodel.service.ThreadManagerService;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;

@Slf4j
public class ActionQueue {
    // 任务数量太多，则报警
    public final static int QUEUE_TOO_MANY_SIZE = 100;

    private final String uid;

    // 游戏中主要是: GameRequestAction
    @Getter
    private final Queue<Action> queue = new ConcurrentLinkedQueue<>();

    @Getter
    private volatile long stopTimestamp;

    @Getter
    private volatile boolean running;

    // 重点!!! 负责启动和取的时候加锁. 会执行调用Action的run方法
    private final SpinLock spinLock = new SpinLock();

    // 重点!!! 负责启动
    private final StartUpQueueTask startUpQueueTask = new StartUpQueueTask(this);
    // 重点!!! 提交下一个
    private final SubmitNextActionTask submitNextActionTask = new SubmitNextActionTask(this);

    @Getter
    private long lastSubmitActTime;

    public ActionQueue(String uid) {
        this.uid = uid;
        this.lastSubmitActTime = 0;
        this.stopTimestamp = System.currentTimeMillis(); // 队列创建出来的时间
        running = false; // 首次初始化
    }

    public void offer(Action action) {
        this.queue.offer(action);

        if (this.queue.size() > QUEUE_TOO_MANY_SIZE) {
            //log.warn("Queue is too many!!! uid={}, queue size is={} ", uid, this.queue.size());
        }

        // 当前还在将action放入queue的request处理线程/用户业务线程
        startUpQueue();
    }

    /**
     * 提交任务到业务线程池, 提交的其实是Callable对象(Action)
     */
    public void submitAction() {
        ThreadManagerService threadManagerService = SpringUtil.getBean(ThreadManagerService.class);

        Action action = queue.poll(); // 从队列中取出一个任务
        long now = System.currentTimeMillis();
        if (action == null) {
            running = false; // 没任务了!!!
            stopTimestamp = now; // 队列中没任务了
            return;
        }

        Future future = null;
        try {
            future = threadManagerService.getActionExecutors().submit(action);  // 重中之重！！！ 这个等于把业务扔到了线程池中，那就会执行了!!!
            lastSubmitActTime = now;
            running = true; // 正常提交成功了!!!
        } catch (Exception e) {
            running = false; // 提交出错了???
            stopTimestamp = now; // 提交失败了
            log.error("ACTION ERROR:" + e.getMessage(), e);
        }

        if (future != null) {
            action.setFuture(future);
        }
    }

    private void startUpQueue() {
        // 只能由一个线程来启动，避免并发启动，同时将复数个action扔入线程池
        spinLock.synchronizeExec(startUpQueueTask);
    }

    public void submitNextAction() {
        spinLock.synchronizeExec(submitNextActionTask);
    }

    public boolean isEmpty() {
        return queue.isEmpty();
    }

    public int getQueueSize() {
        return queue.size();
    }
}
